package crypto

import (
	"bytes"
	cr "crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"math/big"
	"math/rand"
	"net"
	"time"

	"github.com/beherochuling/beesoft_api/app/service/util"
)

type Cert struct {
	PrivateKey  *rsa.PrivateKey
	Certificate *x509.Certificate
	Cert        []byte
	CertPem     *bytes.Buffer
	CertKeyPem  *bytes.Buffer
}

func CreateCA(sub *pkix.Name, expire int) (*Cert, error) {
	var (
		ca  = new(Cert)
		err error
	)

	if expire < 1 {
		expire = 1
	}

	ca.PrivateKey, err = rsa.GenerateKey(cr.Reader, 4096)
	if err != nil {
		return nil, err
	}

	ca.Certificate = &x509.Certificate{
		SerialNumber:          big.NewInt(rand.Int63n(2000)),
		Subject:               *sub,
		NotBefore:             time.Now(),                       // 生效时间
		NotAfter:              time.Now().AddDate(expire, 0, 0), // 过期时间
		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
		IsCA:                  true, // CA
	}

	ca.Cert, err = x509.CreateCertificate(cr.Reader, ca.Certificate, ca.Certificate, &ca.PrivateKey.PublicKey, ca.PrivateKey)
	if err != nil {
		return nil, err
	}

	ca.CertPem = new(bytes.Buffer)
	err = pem.Encode(ca.CertPem, &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: ca.Cert,
	})
	if err != nil {
		return nil, err
	}

	ca.CertKeyPem = new(bytes.Buffer)
	err = pem.Encode(ca.CertKeyPem, &pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(ca.PrivateKey),
	})
	if err != nil {
		return nil, err
	}

	return ca, nil
}
func CreateCert(ca *x509.Certificate, sub *pkix.Name, expire int, dns []string, ip []net.IP) (*Cert, error) {
	var (
		cert = &Cert{}
		err  error
	)

	if expire < 1 {
		expire = 1
	}

	cert.PrivateKey, err = rsa.GenerateKey(cr.Reader, 4096)
	if err != nil {
		return nil, err
	}

	cert.Certificate = &x509.Certificate{
		SerialNumber: big.NewInt(rand.Int63n(2000)),
		Subject:      *sub,
		NotBefore:    time.Now(),
		NotAfter:     time.Now().AddDate(expire, 0, 0),
		KeyUsage:     x509.KeyUsageDigitalSignature,
		ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
		SubjectKeyId: []byte{1, 2, 3, 4, 6},
		IPAddresses:  ip,
		DNSNames:     dns,
	}

	cert.Cert, err = x509.CreateCertificate(cr.Reader, cert.Certificate, ca, &cert.PrivateKey.PublicKey, cert.PrivateKey)
	if err != nil {
		return nil, err
	}

	cert.CertPem = new(bytes.Buffer)
	err = pem.Encode(cert.CertPem, &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: cert.Cert,
	})
	if err != nil {
		return nil, err
	}

	cert.CertKeyPem = new(bytes.Buffer)
	err = pem.Encode(cert.CertKeyPem, &pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(cert.PrivateKey),
	})
	if err != nil {
		return nil, err
	}

	return cert, nil
}

func Export(path string, cert *Cert) {
	util.WriteFile(path+".key", cert.CertKeyPem.Bytes())
	util.WriteFile(path+".crt", cert.CertPem.Bytes())
}
